﻿using System;
using System.Linq;
using CardPlay.Model.ActionModel;
using CardPlay.Model.CardModel;
using CardPlay.Model.GameModel;
using CardPlay.Model.GameModel.Actions;
using CardPlay.Model.GameModel.Presentation;
using CardPlay.Model.Layout;

namespace CardPlay.DemoPack.Games
{
    [Game("260E4C63-37BA-4494-9EBD-238AFB50955B", LevelOfDifficulty.Easy, LevelOfSkill.MostlyChance, DurationOfPlay.LessThan5Minutes)]
    public class FiringSquad : StandardCardGame
    {
        public FiringSquad()
        {
            NamesOfAdditionalPiles.Add(Stock);

            MaximumDeals = 1;
            TotalTableaus = 4;
            TotalFoundations = 0;
        }

        public override ILayoutSite CreatePresentation(IPresentationContext presentation)
        {
            var tableau = presentation.CreateRowLayout();
            presentation.AddTableauPiles(this, tableau);

            var root = new GridLayout
                       {
                           Columns = 3,
                           Rows = 1,
                           HorizontalContentAlignment = HorizontalAlignment.Center,
                           VerticalContentAlignment = VerticalAlignment.Stretch
                       };

            root.Add(tableau);
            root.Add(presentation.CreateMimimalSpacer());
            root.Add(presentation.CreatePileVisual(Stock, presentation.StyleForStock(this)));

            return root;
        }

        public override int PercentComplete
        {
            get
            {
                var maxRank = new int[CardFace.SuitsPerDeck];

                foreach (var card in this.NonEmptyPilesNamed(AnyTableau).Select(pile => pile.TopCard))
                {
                    var suit = (int)card.Suit;
                    maxRank[suit] = Math.Max(GetRanking(card.Rank), maxRank[suit]);
                }

                var cardCount = maxRank.Aggregate(0, (total, value) => total + value);
                return cardCount * 100 / (CardFace.SuitsPerDeck * CardFace.RanksPerSuit);
            }
        }

        protected override bool IsOver
        {
            get { return this[Stock].IsEmpty && !IsMovePossible; }
        }

        protected override bool IsComplete
        {
            get { return this[Stock].IsEmpty && PilesNamed(AnyTableau).All(pile => pile.Count == 1); }
        }

        protected override IAction QueryActionAt(PartLocation location)
        {
            var card = CardAt(location);
            if (card == null) return null;

            if (location.Matches(Stock))
            {
                if (!IsMovePossible)
                {
                    return new DealOutTableauAction(this, location);
                }
            }
            else if (!IsHighestRank(card.Rank))
            {
                // If a higher ranking card of the same suit exists in another
                // pile, remove the selected card.

                var topCards = this.NonEmptyPilesNamed(AnyTableau)
                    .Where(pile => pile.Name != location.Path)
                    .Select(pile => pile.TopCard);

                if (topCards.Any(c => c.Suit == card.Suit && GetRanking(c.Rank) > GetRanking(card.Rank)))
                {
                    return new RemoveCardAction(this, location);
                }
            }

            return null;
        }

        protected override void OverridePlayingRules(PlayingRules playingRules)
        {
            // Customise tableau building regulation.
            playingRules.At(AnyTableau).Permit<MovePileAction>().When(action => action.TargetPile.IsEmpty);
        }

        protected override void PrepareDealScript(DealScript script)
        {
            script.DealCardToEach(NamesOfTableaus);
            script.DealRemainingFaceDownTo(Stock);
        }

        private static bool IsHighestRank(Rank rank)
        {
            return rank == Rank.Ace;
        }

        private static int GetRanking(Rank rank)
        {
            return rank == Rank.Ace ? CardFace.RanksPerSuit : (int)(rank - 1);
        }

        private bool IsMovePossible
        {
            get
            {
                foreach (var target in this.NonEmptyPilesNamed(AnyTableau))
                {
                    if (target.IsEmpty)
                    {
                        return this.NonEmptyPilesNamed(AnyTableau).Max(pile => pile.Count) > 1;
                    }

                    if (QueryActionAt(target.Top) is RemoveCardAction)
                    {
                        return true;
                    }
                }

                return false;
            }
        }
    }
}